<?php
/**
 * Author:xsc
 * Time:2023/9/1 17:06
 */

namespace App\Extensions\Coupon;

use App\Extensions\BaseService;
use App\Common\ResponseCode;
use App\Exceptions\BusinessException;
use Illuminate\Support\Facades\Log;
use App\Traits\HttpHelper;
use App\Jobs\ApplylogJob;

class CouponService extends BaseService
{

    use HttpHelper;

    private $url;

    private $salt;

    // 汇筑的渠道编码
    private $hz_channel_code;

    private $public_key_path;

    private $private_key_path;

    public function __construct()
    {
        $this->url = config('coupon.url');
        $this->salt = config('coupon.salt');
        $this->hz_channel_code = config('coupon.channel_code');

        if (env('APP_ENV') == 'pro') {
            $app_env = 'pro';
        } else {
            $app_env = 'test';
        }
        $this->public_key_path = base_path() . '/keys/coupon/' . $app_env . '_public.pem';
        $this->private_key_path = base_path() . '/keys/coupon/' . $app_env . '_private.pem';
    }

    /**
     * 货品列表
     * @param string $channel_code
     * @param string $sku_name
     * @param int $filter_red_envelope
     * @param int $page
     * @param int $page_size
     * @return array|mixed
     * @throws BusinessException
     * Author:xsc
     * Time:2023/11/16 14:54
     */
    public function listChannelSku(string $channel_code, string $sku_name = '', int $filter_red_envelope = 0, int $page = 0, int $page_size = 0)
    {
        $url = 'api/listChannelSku';

        $offset = 0;
        $limit = 10000;
        if ($page && $page_size) {
            $offset = ($page - 1) * $page_size;
            $limit = $page_size;
        }

        $params = [
            'channelCode' => $channel_code,
            'data'        => [
                'offset'     => $offset,
                'limit'      => $limit,
                'showDetail' => 1,
                'showAmount' => 1,
            ],
        ];

        if ($sku_name) {
            $params['data']['skuName'] = $sku_name;
        }

        if ($filter_red_envelope == 1) {
            $params['data']['filterRedEnvelope'] = 1;
        }

        $res = $this->request($url, $params);

        $data = ['total' => 0, 'rows' => [],];
        if ($res['code'] == '0000' && $res['data']) {
            $data = $res['data'];
        }
        return $data;
    }

    /**
     * 获取单个货品
     * @param string $channel_code
     * @param string $sku_code
     * @return array|mixed
     * @throws BusinessException
     * Author:xsc
     * Time:2023/9/11 19:27
     */
    public function getChannelSku(string $channel_code, string $sku_code)
    {
        $url = 'api/getChannelSku';

        $params = [
            'channelCode' => $channel_code,
            'data'        => [
                'skuCode'    => $sku_code,
                'showDetail' => 1,
                'showAmount' => 1,
            ],
        ];

        $res = $this->request($url, $params);

        $detail = [];
        if ($res['code'] == '0000') {
            $detail = $res['data'];
        }
        return $detail;
    }

    /**
     * 获取单个货品库存
     * @param string $channel_code
     * @param string $sku_code
     * @return mixed|null
     * @throws BusinessException
     * Author:xsc
     * Time:2023/9/11 19:29
     */
    public function getStock(string $channel_code, string $sku_code)
    {
        $url = 'api/getStock';

        $params = [
            'channelCode' => $channel_code,
            'data'        => [
                'skuCode' => $sku_code,
            ],
        ];

        $res = $this->request($url, $params);

        $stock = null;
        if ($res['code'] == '0000') {
            $stock = $res['data']['stock'];
        }
        return $stock;
    }

    /**
     * 创建红包
     * @param $channel_code
     * @param $sku_code
     * @param $sku_name
     * @param $type
     * @param $stock
     * @param $end_date
     * @param $amount
     * @return bool
     * @throws BusinessException
     * Author:xsc
     * Time:2023/9/8 19:48
     */
    public function createRedEnvelope($channel_code, $sku_code, $sku_name, $type, $stock, $end_date, $amount = 0)
    {
        $url = 'api/createRedEnvelope';

        $params = [
            'channelCode' => $channel_code,
            'data'        => [
                'merchantNo'   => $channel_code,
                'redEnvelopes' => [
                    [
                        'skuCode' => $sku_code,
                        'skuName' => $sku_name,
                        'type'    => $type,
                        'stock'   => $stock,
                        'endDate' => $end_date,
                    ],
                ],
            ],
        ];

        if ($amount) {
            $params['data']['redEnvelopes'][0]['amount'] = $amount;
        }

        $res = $this->request($url, $params);
        if ($res['code'] == '0000') {
            return true;
        }
        return false;
    }

    /**
     * 购买卡券
     * @param array $data
     * @return array|mixed
     * @throws BusinessException
     * Author:xsc
     * Time:2023/10/19 16:50
     */
    public function buySku(array $data)
    {
        $url = 'api/buySku';

        $channel_code = isset($data['channel_code']) ? $data['channel_code'] : $this->hz_channel_code;

        $params = [
            'channelCode' => $channel_code,
            'data'        => [
                'skuCode'      => $data['sku_code'],
                'couponNum'    => $data['coupon_num'],
                'noticeUrl'    => $data['notice_url'],
                'phone'        => $data['phone'],
                'thirdNo'      => $data['third_no'],
                'collectionNo' => $data['collection_no'],
            ],
        ];

        $res = $this->request($url, $params);
        if ($res['code'] == '0000' && isset($res['data'])) {
            return $res['data'];
        }
        return [];
    }

    /**
     * 核销码查看
     * @param string $sku_code
     * @param string $msg_code
     * @param string $channel_code
     * @return array|mixed
     * @throws BusinessException
     * Author:xsc
     * Time:2023/10/11 15:27
     */
    public function checkCard(string $sku_code, string $msg_code, string $channel_code = '')
    {
        $url = 'api/card/checkCard';

        $channel_code = $channel_code ?: $this->hz_channel_code;

        $params = [
            'channelCode' => $channel_code,
            'data'        => [
                'skuCode'    => $sku_code,
                'msgCode'    => $msg_code,
                'showDetail' => 1,
            ],
        ];

        $res = $this->request($url, $params);
        if ($res['code'] == '0000' && isset($res['data'])) {
            return $res['data'];
        }
        return [];
    }

    /**
     * 券码冻结/解冻
     * @param string $sku_code
     * @param string $msg_code
     * @param int $opera_type 0冻结,1解冻
     * @param string $channel_code
     * @return bool
     * @throws BusinessException
     * Author:xsc
     * Time:2023/10/11 15:28
     */
    public function operaCode(string $sku_code, string $msg_code, int $opera_type, string $channel_code = '')
    {
        $url = 'api/card/operaCode';

        $channel_code = $channel_code ?: $this->hz_channel_code;

        $params = [
            'channelCode' => $channel_code,
            'data'        => [
                'skuCode'   => $sku_code,
                'msgCode'   => $msg_code,
                'operaType' => $opera_type,
            ],
        ];

        $res = $this->request($url, $params);
        if ($res['code'] == '0000') {
            return true;
        }
        return false;
    }

    /**
     * 转让更新接口
     * @param string $sku_code
     * @param string $msg_code
     * @param string $third_no
     * @param string $phone
     * @param string $channel_code
     * @return array|mixed
     * @throws BusinessException
     * Author:xsc
     * Time:2023/10/19 15:12
     */
    public function changeMsgCode(string $sku_code, string $msg_code, string $third_no, string $phone, string $channel_code = '')
    {
        $url = 'api/card/changeMsgCode';

        $channel_code = $channel_code ?: $this->hz_channel_code;

        $params = [
            'channelCode' => $channel_code,
            'data'        => [
                'skuCode' => $sku_code,
                'msgCode' => $msg_code,
                'thirdNo' => $third_no,
                'phone'   => $phone,
            ],
        ];

        $res = $this->request($url, $params);
        if ($res['code'] == '0000' && isset($res['data'])) {
            return $res['data'];
        }
        return [];
    }

    /**
     * 批量查看
     * @param array $coupons
     * @param string $channel_code
     * @return array|mixed
     * @throws BusinessException
     * Author:xsc
     * Time:2023/11/30 14:21
     */
    public function batchCheckCard(array $coupons, string $channel_code = '')
    {
        $url = 'api/card/batchCheckCard';

        $channel_code = $channel_code ?: $this->hz_channel_code;

        foreach ($coupons as &$value) {
            $value['skuCode'] = $value['sku_code'];
            $value['msgCode'] = $value['msg_code'];
            unset($value['sku_code'], $value['msg_code']);
        }

        $params = [
            'channelCode' => $channel_code,
            'data'        => [
                'showDetail' => 1,
                'coupons'    => $coupons,
            ],
        ];

        $res = $this->request($url, $params);

        if ($res['code'] == '0000' && isset($res['data']['coupons'])) {
            return $res['data']['coupons'];
        } else {
            throw new BusinessException(ResponseCode::COUPON_NOT_FUND, ['error' => '卡券系统:' . $res['msg']]);
        }
    }

    /**
     * 批量冻结券码
     * @param array $coupons
     * @param int $opera_type
     * @param string $channel_code
     * @return true
     * @throws BusinessException
     * Author:xsc
     * Time:2023/12/1 14:30
     */
    public function batchOperaCode(array $coupons, int $opera_type = 0, string $channel_code = '')
    {
        $url = 'api/card/batchOperaCode';

        $channel_code = $channel_code ?: $this->hz_channel_code;

        foreach ($coupons as &$value) {
            $value['skuCode'] = $value['sku_code'];
            $value['msgCode'] = $value['msg_code'];
            unset($value['sku_code'], $value['msg_code']);
        }

        $params = [
            'channelCode' => $channel_code,
            'data'        => [
                'operaType' => $opera_type,
                'coupons'   => $coupons,
            ],
        ];

        $res = $this->request($url, $params);

        if ($res['code'] == '0000') {
            return true;
        } else {
            throw new BusinessException(ResponseCode::COUPON_FREEZE_FAIL, ['error' => '卡券系统:' . $res['msg']]);
        }
    }

    /**
     * 订单列表
     * @param string $phone
     * @param string $channel_code
     * @return array|mixed
     * @throws BusinessException
     * Author:xsc
     * Time:2023/12/1 9:54
     */
    public function listOrder(string $phone, string $channel_code = '')
    {
        $url = 'api/listOrder';

        $channel_code = $channel_code ?: $this->hz_channel_code;

        $params = [
            'channelCode' => $channel_code,
            'data'        => [
                'phone' => $phone,
            ],
        ];

        $res = $this->request($url, $params);
        if ($res['code'] == '0000' && isset($res['data'])) {
            return $res['data'];
        }
        return [];
    }

    /**
     * 调用卡券接口
     * @param string $url
     * @param array $data
     * @return mixed
     * @throws BusinessException
     * Author:xsc
     * Time:2023/9/8 20:00
     */
    private function request(string $url, array $data)
    {
        $time1 = microtime(true);

        if (empty($url) || empty($data)) {
            throw new BusinessException(ResponseCode::COUPON_PARAMS_ERROR);
        }

        $url = $this->url . $url;
        $url_arr = explode('/', $url);
        $action = end($url_arr);

        $data['sign'] = $this->sign($data);
        Log::info('[卡券请求参数]' . $action . json_encode($data, JSON_UNESCAPED_UNICODE));

        try {
            $res = $this->sendPost($url, $data)->getBody()->getContents();
        } catch (\GuzzleHttp\Exception\GuzzleException $exception) {
            throw new BusinessException(ResponseCode::COUPON_REQUEST_ERROR);
        }

        if (!$res) {
            throw new BusinessException(ResponseCode::COUPON_REQUEST_ERROR);
        }

        Log::info('[卡券返回数据]' . $action . $res);

        // 异步记录请求日志
        $log = [
            'action'      => $url,
            'apply_info'  => json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
            'result_info' => $res,
            'create_time' => date('Y-m-d H:i:s'),
            'type'        => '卡券'
        ];
        dispatch(new ApplylogJob($log));

        $time2 = microtime(true);
        Log::info('[卡券请求耗时]' . $action . ':' . intval(($time2 - $time1) * 1000) . 'ms');

        return json_decode($res, true);
    }

    /**
     * 签名
     * @param array $data
     * @return string
     * Author:xsc
     * Time:2023/9/8 19:59
     */
    private function sign(array $data)
    {
        $sign_str = $this->getSignStr($data);

        // 公钥
        $public_key = file_get_contents($this->public_key_path);

        // 转换为openssl格式
        $public_key = openssl_get_publickey($public_key);

        // 公钥加密
        openssl_public_encrypt($sign_str, $encrypt_data, $public_key, OPENSSL_PKCS1_PADDING);

        return base64_encode($encrypt_data);
    }

    /**
     * 获取签名字符串
     * @param array $data
     * @return string
     * Author:xsc
     * Time:2023/11/30 17:25
     */
    private function getSignStr(array $data)
    {
        $arr = $this->getSignArr($data);
        sort($arr, SORT_STRING);
        $str = implode('&', $arr);
        return md5($str . $this->salt);
    }

    /**
     * 获取参与签名的数据
     * @param array $data
     * @return array
     * Author:xsc
     * Time:2023/11/30 17:25
     */
    private function getSignArr(array $data)
    {
        $arr = [];
        foreach ($data as $key => $value) {
            if (is_array($value)) {
                $arr = array_merge($arr, $this->getSignArr($value));
            } else {
                // 存在同名key时,kv格式的数组会覆盖同名key,所以这样写
                array_push($arr, $key . '=' . $value);
            }
        }
        return $arr;
    }

    /**
     * 解密
     * @param string $encrypted_data
     * @return string
     * Author:xsc
     * Time:2023/10/19 17:46
     */
    public function decrypt(string $encrypted_data)
    {
        $private_key = file_get_contents($this->private_key_path);

        openssl_private_decrypt(base64_decode($encrypted_data), $decrypted_data, $private_key, OPENSSL_PKCS1_PADDING);

        return $decrypted_data;
    }

}
